#include <stdio.h>		/// printf, strout, puts, fileno and so on
#include <stdlib.h>		/// exit
#include <string.h>		/// strchr, strcmp, strlen
#include <unistd.h>		/// pipe, fork, close, dup2, read and so on
#include <netinet/in.h>	/// socket and so on
#include <sys/wait.h>	/// waitpid
#define MY_PORT		5413
#define MY_PROMPT	"\r\n> "
#define MY_PROLEN	4

int exec_cmd(char *pCmd, int cmdLen, int fd)
{
	int pipe_fd[2];
	if ((pipe(pipe_fd)) == -1)
	{
		perror("pipe init error");
		return -1;
	}

	while (pCmd[cmdLen - 1] == '\r' || pCmd[cmdLen - 1] == '\n')
	{
		pCmd[--cmdLen] = 0;
	}

	int argc = 0;
	char* argv[8] = {0};
	while (pCmd != NULL)
	{
		char* nextArg = strchr(pCmd, ' ');
		if (nextArg != NULL)
			*nextArg++ = 0;
		argv[argc++] = pCmd;
		pCmd = nextArg;
	}

	if (strcmp(argv[0], "cd") == 0)
	{
		chdir(argv[1]);
		return 0;
	}
	else if (strcmp(argv[0], "exit") == 0)
	{
		close(fd);
		return 10;
	}

	pid_t pid = fork();
	if (pid == -1)
	{
		perror("fork error");
		exit(1);
	}
	else if (pid == 0)
	{	/// 子进程
		close(pipe_fd[0]);	/// 通过无名管道对stdou、stderr输出重定向
		dup2(pipe_fd[1], fileno(stderr));
		dup2(pipe_fd[1], fileno(stdout));
		execvp(argv[0], argv);
		close(pipe_fd[1]);
		exit(0);
	}
	else
	{	/// 父进程
		close(pipe_fd[1]);
		char buf_cmd[2048];
		int len = 0;
		while ((len = read(pipe_fd[0], buf_cmd, sizeof(buf_cmd))) > 0)
		{
			buf_cmd[len] = 0;
			write(fd, buf_cmd, len);
		}
		waitpid(pid, NULL, 0);
		return 0;
	}
}

int main(void)
{
	struct sockaddr_in netaddr;
	int socket_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	netaddr.sin_family = AF_INET;
	netaddr.sin_port = htons(MY_PORT);
	netaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	int reuseopt = 1;
	setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR,
			   (char*)&reuseopt, sizeof(&reuseopt));
	bind(socket_fd, (struct sockaddr *)&netaddr, sizeof(struct sockaddr));
	listen(socket_fd, 3);
	printf("Mini Telnetd Port[%d]\n", MY_PORT);
	char buf[1024] = {0};
	while (1)
	{
		unsigned int len = sizeof(struct sockaddr);
		struct sockaddr_in cliaddr;
		int connfd = accept(socket_fd, (struct sockaddr *)&cliaddr, &len);
		if (connfd < 0)
			continue;
		write(connfd, MY_PROMPT, MY_PROLEN);	/// 打印提示符
		while ((len = read(connfd, buf, sizeof(buf))) > 0)
		{
			buf[len] = 0;
			if (exec_cmd(buf, strlen(buf), connfd) == 10)
				break;
			write(connfd, MY_PROMPT, MY_PROLEN);	/// 打印提示符
		}
	}
	return 0;
}
